package controllers;

import java.io.DataOutputStream;
import java.io.IOException;
import java.net.Socket;
import java.util.Enumeration;
import java.util.Random;

import play.Logger;
import play.mvc.*;

import com.fasterxml.jackson.databind.JsonNode; 

import views.html.*;
import models.*;
import models.msgs.Info;

public class Application extends Controller {
	public final static int numberOfPlayers = 2; 
	static int numberOfCurrentPlayers = 0;
	static boolean allowNewPlayers = true,
			gameStarted = false;
	
	static boolean[] playerStatus = null;

	static int currentPlayerId = 0,	
			numberOfAllInPlayers = 0,
			numberOfPlayersPlayAgain = 0,
			numberOfPlayersWhoFolded = 0,
			numberOfChips = 1000,
			dealerButtonHolder = 0,
			smallBlind = 3,
			bigBlind = 2 * smallBlind,
			smallBlindHolder = 0,
			bigBlindHolder = 0,
			maxBetHolder = 0,
			movesThisRound = 0,
			nextPlayerMoveID = 0,
			gamePot = 0,
			currentRoundPot = 0,
			currentRound = 1,
			currentGame = 1;
	
	static int[] betsAllGame, 
			playerChips = null, 
			betsThisRound;
	
	static String[] currentDeck = new String[52],
			putDeck = new String[0],
			players = new String[0],
			playerCards = null;
	
	static String[][] playerMoves;
  
    /**
     * Display the home page.
     */
	
    public static Result index() {
    	
        return ok(index.render());
    }
  
    /**
     * Display the chat room.
     */
    
    public static void initialize() {
    	numberOfCurrentPlayers++;
    	System.out.println("Welcome player nr "+numberOfCurrentPlayers);
    	
    	if(numberOfCurrentPlayers == numberOfPlayers) {
         	System.out.println("Start the game...");
         	players = new String[numberOfPlayers];
    		playerCards = new String[numberOfPlayers*4];
    		playerChips = new int[numberOfPlayers];
    		playerStatus = new boolean[numberOfPlayers];
    		playerMoves = new String[numberOfPlayers][2];
    		betsThisRound = new int[numberOfPlayers];
    		betsAllGame = new int[numberOfPlayers];
    				
    		currentDeck = newDeckOfCards();
    		currentDeck = shuffleDeck(currentDeck);
    		
    		handOfCards(numberOfPlayers);
    		allocateChips(numberOfPlayers, numberOfChips);
    		setPlayersStatus(numberOfPlayers);
    		beginNewGame();
        }
        else if(numberOfCurrentPlayers > numberOfPlayers) {
         	System.out.println("Too many players...");
        }
    	
    }
    
    public static Result chatRoom(String username) {
    	
        if(username == null || username.trim().equals("")) {
            flash("error", "Please choose a valid username.");
            return redirect(routes.Application.index());
        }
        initialize();
        return ok(chatRoom.render(username));    
    }
    
  
    public static Result chatRoomJs(String username) {
        return ok(views.js.chatRoom.render(username));
    }
    
    /**
     * Handle the chat websocket.
     */
    public static WebSocket<JsonNode> chat(final String username) {
        return new WebSocket<JsonNode>() {
            
            public void onReady(WebSocket.In<JsonNode> in, WebSocket.Out<JsonNode> out){
                
            	Table.notifyAll(new Info("I've got a WebSocket", username));
            	
                // Join the chat room.
                try {                	
                	Table.join(username, in, out);
                	
                } catch (Exception ex) {
                    ex.printStackTrace();
                }
            }
        };
    }
    
    /** 
  	 * Sets every player status to false (is not active) on the beginning. 
  	 */
  	static void setPlayersStatus(int numberOfPlayers) {
  		for (int i = 0; i < numberOfPlayers; i++) 
  			playerStatus[i] = false;
  	}
  	
	/**
	 * Gives every player the same amount of chips.
	 */
	static void allocateChips(int numberOfPlayers, int numberOfChips) {
		for (int i = 0; i < numberOfPlayers; i++) 
			playerChips[i] = numberOfChips;
	}

    
	private static String prepareNewGame() {
		if (numberOfCurrentPlayers == 0) {
			return "";
		}
		
		if (numberOfPlayersWhoFolded > 0 && !allowNewPlayers) {
			allowNewPlayers = true;
		}
		
		String playersCanPlayAgain = "";
		int numberOfPlayersCanPlayAgain = 0;
		
		System.out.println("   Players, who can play 1 more game:");
		
		for (int i = 0; i < numberOfPlayers; i++) {
			if (players[i] != null && playerChips[i] > bigBlind) {
				playersCanPlayAgain += " " + (i+1);
				System.out.print("   " + players[i]);
				numberOfPlayersCanPlayAgain += 1;
			} else {
				playerStatus[i] = false;
			}
		}
		System.out.println();
		
		
		if (numberOfPlayersCanPlayAgain < numberOfPlayers && !allowNewPlayers) {
			allowNewPlayers = true;
		}
		
		numberOfCurrentPlayers = 0;
		numberOfAllInPlayers = 0;
		numberOfPlayersWhoFolded = 0;
		
		currentRound = 1;
		currentGame += 1;
		
		movesThisRound = 0;
		currentRoundPot = 0;
		gamePot = 0;
		
		for (int i = 0; i < numberOfPlayers; i++) {
			if (players[i] != null) {
				playerStatus[i] = true;
			}
		}
		
		
		if (gameStarted)
			return "";

		playerCards = new String[numberOfPlayers*4];
		playerMoves = new String[numberOfPlayers][2];
		betsThisRound = new int[numberOfPlayers];
		betsAllGame = new int[numberOfPlayers];
		
		betsThisRound = new int[numberOfPlayers];
		playerMoves = new String[numberOfPlayers][2];
		
		dealerButtonHolder = 0;
		smallBlindHolder = 0;
		bigBlindHolder = 0;
		maxBetHolder = 0;
		
		currentDeck = newDeckOfCards();
		currentDeck = shuffleDeck(currentDeck);
		
		handOfCards(numberOfPlayers);
		return playersCanPlayAgain;
	}
	
	
	/** 
	 * Creates a new deck of cards. 
	 * @return Returns new deck of cards as an array.
	 */
	static String[] newDeckOfCards() {
		String[] newDeck = new String[52];
		
		int[] numbers = new int[]{1,2,3,4,5,6,7,8,9,10,11,12,13};
		String[] colors = new String[]{"Red-Heart", "Red-Diamond", "Black-Spade", "Black-Club"};
		
		int index = 0;
		
		for (int i = 0; i < numbers.length; i++) {
			for (int j = 0; j < colors.length; j++) {
				newDeck[index] = numbers[i] + "-" + colors[j];
				index += 1;
			}
		}
		
		return newDeck;
	}
	
	/** 
	 * Shuffles a deck of cards. 
	 * @param oldDeck A deck of cards (as an array) to shuffle.
	 * @return Shuffled deck of cards.
	 */
	private static String[] shuffleDeck(String[] oldDeck) {
		Random random = new Random();
		
	    for (int i = oldDeck.length - 1; i > 0; i--) {
	      int index = random.nextInt(i + 1);
	     
	      String oldIndexValue = oldDeck[index];
	      
	      oldDeck[index] = oldDeck[i];
	      oldDeck[i] = oldIndexValue;
	    }
	    
	    return oldDeck;
	}
	
	/** 
	 * First hand of cards. 
	 */
	private static void handOfCards(int numberOfPlayers) {
		for (int i = 0; i < numberOfPlayers*4; i++) {
			playerCards[i] = currentDeck[i];
			currentDeck[i] = null;
		}
		
		sliceAllNulls(currentDeck);
	}
	
	
	/** 
	 * Removes all null values from input array (shorten an array).
	 * @param deck An array with null values.
	 */
	static void sliceAllNulls(String[] deck) {
		int index = 0;
		int newLength = 0;
		String[] newDeck = null;
		
		for (int i = 0; i < deck.length; i++) {
			if (deck[i] != null)
				newLength += 1;
		}
		
		newDeck = new String[newLength];
		
		for (int i = 0; i < deck.length; i++) {
			if (deck[i] != null) {
				newDeck[index] = deck[i];
				index += 1;
			}
		}
		
		currentDeck = new String[newLength];
		currentDeck = newDeck;
	}
	
	/**
	 * Sends an information that new game is ready, set dealer button and blinds
	 * holders.
	 */
	synchronized static void beginNewGame() {
		gameStarted = true;
		allowNewPlayers = false;
		
		String data;
		
		System.out.println(" > Start of the game (" + currentGame + ").");

		
		setDealerButtonHolder();
		setBlindsHolders();
		
		nextPlayerMoveID = nextPlayer(bigBlindHolder);
		System.out.println("test");
		
		data = "newRound 1 ";
		data += nextPlayerMoveID + " ";
		data += players[nextPlayerMoveID-1] + " ";
		data += dealerButtonHolder + " ";
		data += smallBlindHolder + " " + smallBlind + " ";
		data += bigBlindHolder + " " + bigBlind + " ";
		data += possiblePlayerMoves(nextPlayerMoveID);
		
		//Table.notifyAll(new Info("startOfTheGame " + currentGame, "server"));

	}
	
	/** 
	 * Sets, who has a dealer button.
	 */
	private static void setDealerButtonHolder() {
		if (currentRound == 1) {
			Random rand = new Random();
			dealerButtonHolder = rand.nextInt(numberOfPlayers) + 1;
		} else {
			dealerButtonHolder = nextPlayer(dealerButtonHolder);
		}
	}
	

	/** 
	 * Sets, which players owns small and big blind.
	 */
	private static void setBlindsHolders() {
		
		smallBlindHolder = nextPlayer(dealerButtonHolder);
		
		bigBlindHolder = nextPlayer(smallBlindHolder);

		
		
		if (currentRound == 1) {
			playerChips[smallBlindHolder-1] -= smallBlind;
			playerChips[bigBlindHolder-1] -= bigBlind;

			gamePot += smallBlind + bigBlind;
			currentRoundPot += smallBlind + bigBlind;
			
			betsThisRound[smallBlindHolder-1] = smallBlind;
			betsThisRound[bigBlindHolder-1] = bigBlind;
			
			betsAllGame[smallBlindHolder-1] = smallBlind;
			betsAllGame[bigBlindHolder-1] = bigBlind;
			
			playerMoves[smallBlindHolder-1] = new String[]{"bet", smallBlind + ""};
			playerMoves[bigBlindHolder-1] = new String[]{"raise", (bigBlind - smallBlind) + ""};
			
			maxBetHolder = bigBlindHolder;

		} else {
			nextPlayerMoveID = smallBlindHolder;
		}
	}
	
	/** 
	 * Finds an ID of player who is "sitting" on the left of player with input ID.
	 * @param ID
	 * @return Returns an ID of found player. 
	 */
	static int nextPlayer(int ID) {
		int nextPlayerID = ID % numberOfPlayers + 1;
		
		while (playerStatus[nextPlayerID-1] == false) {
			nextPlayerID = nextPlayerID % numberOfPlayers + 1;	
		}
		
		nextPlayerMoveID = nextPlayerID;
		return nextPlayerID;
	}
	
	/** 
	 * Return possible (next) player moves.
	 */
	static String possiblePlayerMoves(int pID) {
		if (currentRound == 1) {
			
			if (playerChips[pID-1] > 0) {
				int playerBet = playerChips[pID-1] + betsThisRound[pID-1];
				int maxBet = maxBetThisRound();
				
				if (pID == bigBlindHolder && maxBet <= bigBlind) {
				
					if (playerBet > maxBet)
						return "true false false true true";
					else
						return "true false false false true";
				} else {

					if (playerBet > maxBet && pID != maxBetHolder)
						return "false false true true true";
					else if (playerBet == maxBet && pID != maxBetHolder)
						return "false false true false true";
					else
						return "false false false false true";
				}

			} else {
				return "false false false false false";
			}
			
		} else {
			if (currentRoundPot == 0) {

				if (playerChips[pID-1] > 0) {
					return "true true false false true";
				} else {
					return "true true false false false";
				}

			} else {
				if (playerChips[pID-1] > 0) {
					int playerBet = playerChips[pID-1] + betsThisRound[pID-1];
					int maxBet = maxBetThisRound();
					
					
					if (playerBet > maxBet && pID != maxBetHolder)
						return "false false true true true";
					else if (playerBet == maxBet && pID != maxBetHolder)
						return "false false true false true";
					else
						return "false false false false true";
				} else {
					return "false false false false false";
				}
			}
		}
	}
	
	/** 
	 * @return Returns a max bet in current round.
	 */
	static int maxBetThisRound() {
		try {
			return betsThisRound[maxBetHolder-1];
		} catch (Exception e) {
			return 0;
		}
	}
	
	
	/** 
	 * Sends a message to all active users.
	 */
	
	/*
	static void sendToAll(String message) {
		synchronized (outputStreams) {

			for (Enumeration<DataOutputStream> en = outputStreams.elements(); en.hasMoreElements();) {
				DataOutputStream writer = (DataOutputStream)en.nextElement();
       
				try {
					writer.writeUTF(message);
				} catch(IOException e) { }
			}
		}
	}
	
	
	/** 
	 * Removes a connection, when 'fold' or connection error. 
	 * @param playerSocket Player's socket (to find in 'outputStream' and delete).
	 * @param playerName
	 * @param playerID
	 */
	/*
	static void removeConnection(Socket playerSocket, String playerName, int playerID) {
		if (outputStreams.get(playerSocket) == null)
			return;
		
		synchronized (outputStreams) {
			System.out.println(" > Deleting " + playerName + "'s socket.");

			outputStreams.remove(playerSocket);
			playerStatus[playerID-1] = false;
			players[playerID-1] = null;
			
			try {
				playerSocket.close();
			} catch(IOException e) {
				System.out.println(" > Error while closing " + playerName + "'s socket.");
			}
			
		}
	}

	
	/** 
	 * Check if end of current round/ game.
	 */
	/*
	static String[] isEndOf(int pID) {
		if (movesThisRound < numberOfCurrentPlayers + numberOfAllInPlayers && currentRound % 2 == 0)
			return new String[]{"false", "false"};
		else if (movesThisRound < numberOfCurrentPlayers)
			return new String[]{"false", "false"};
		else {
			if (currentRound % 2 == 1) {
				int prevVal = -1;
				
				for (int i = 0; i < numberOfPlayers; i++) {
					if (playerMoves[i][0] != null && !playerMoves[i][0].equals("null")) {
						if (!playerMoves[i][0].equals("allIn") && !playerMoves[i][0].equals("fold")) {
							if (betsThisRound[i] != prevVal && prevVal > -1) {
								return new String[]{"false", "false"};
							} else if (prevVal == -1) {
								prevVal = betsThisRound[i];
							}	
						}
					}
				}
				
				
				if (currentRound == 7) {
					String allCardsStr = "";
					
					for (int i = 0; i < numberOfPlayers; i++) {
						allCardsStr += " " + players[i];
						
						String[][] c = sortCards(new String[][]{
								playerCards[4*i].split("-"),
								playerCards[4*i+1].split("-"),
								playerCards[4*i+2].split("-"),
								playerCards[4*i+3].split("-")});
						
						for (int j = 0; j <= 3; j++)
							allCardsStr += " " + c[j][0] + "-" + c[j][1] + "-" + c[j][2];
					}
					
					sendToAll("tableUpdate showCards" + allCardsStr);
					gameStarted = false;
					
					return new String[]{"true", "true"};
				} else {
					betsThisRound = new int[numberOfPlayers];
					playerMoves = new String[numberOfPlayers][2];
					nextPlayerMoveID = 0;
					movesThisRound = 0;
					currentRoundPot = 0;
					maxBetHolder = 0;
					
					currentRound += 1;
					
					return new String[]{"true", "false", (currentRound-1) + ""};
				}
			} else {
				betsThisRound = new int[numberOfPlayers];
				playerMoves = new String[numberOfPlayers][2];
				nextPlayerMoveID = 0;
				movesThisRound = 0;
				currentRoundPot = 0;
				maxBetHolder = 0;
				
				currentRound += 1;
				
				setDealerButtonHolder();
				setBlindsHolders();
				
				sendToAll("tableUpdate dealerAndBlinds " + dealerButtonHolder + " " + smallBlindHolder + " " + bigBlindHolder);
				
				return new String[]{"true", "false", (currentRound-1) + ""};
			}
		}
	}
	
	
	/** 
	 * Checks and remembers player's move. Send information and table updates
	 * about registered move.
	 * @param playerID ID of player who made a move.
	 * @param writer An output stream to write response.
	 * @param action Player's move as a String.
	 * @param amount Amount (when action was 'bet' or 'raise') or String of ID's of cards
	 * which is going to be drew
	 * @return false: if player's move was forbidden, true: in other case.
	 */
	/*
	static boolean registerPlayerMove(int playerID, DataOutputStream writer, String action, String amount) throws IOException {
		
		if (!action.equalsIgnoreCase("drawMyCards") && !action.equalsIgnoreCase("dontDrawMyCards")) {
			
			int amountInt;

			try {
				amountInt = Integer.parseInt(amount);
			} catch (Exception e) {
				amountInt = 0;
			}
			
			if (action.equals("check")) {
				movesThisRound += 1;
				writer.writeUTF("actionOk");
				sendToAll("tableUpdate playerBet " + playerID + " " + betsThisRound[playerID-1]);
				sendToAll(color("&nbsp; Player <b>" + players[playerID-1] + "</b> played <i>Check</i>.", "white"));

			} else if (action.equals("bet")) {
				if (amountInt >= playerChips[playerID-1]) {
					writer.writeUTF("actionError");
					return false;
				}
				
				gamePot += amountInt;
				betsThisRound[playerID-1] += amountInt;
				betsAllGame[playerID-1] += amountInt;
				playerChips[playerID-1] -= amountInt;
				currentRoundPot += amountInt;
				maxBetHolder = playerID;
				movesThisRound += 1;

				sendToAll("tableUpdate potAndMaxBet " + gamePot + " " + maxBetThisRound());
				writer.writeUTF("actionOk");
				writer.writeUTF("yourChips " + playerChips[playerID-1]);
				writer.writeUTF("tableUpdate yourBet " + betsAllGame[playerID-1]);	
				sendToAll("tableUpdate playerBet " + playerID + " " + betsThisRound[playerID-1]);
				sendToAll(color("&nbsp; Player <b>" + players[playerID-1] + "</b> played <i>Bet ($" + amount + ")</i>.", "white"));

			} else if (action.equals("call") && maxBetHolder != playerID) {
				int maxBet = maxBetThisRound();
				int thisMoveBet = maxBet - betsThisRound[playerID-1];
				
				if (thisMoveBet >= playerChips[playerID-1]) {
					writer.writeUTF("actionError");
					return false;
				}

				gamePot += thisMoveBet;
				betsThisRound[playerID-1] = maxBet;
				betsAllGame[playerID-1] += thisMoveBet;
				playerChips[playerID-1] -= thisMoveBet;
				currentRoundPot += thisMoveBet;
				maxBetHolder = playerID;
				movesThisRound += 1;

				sendToAll("tableUpdate potAndMaxBet " + gamePot + " " + maxBetThisRound());
				writer.writeUTF("actionOk");
				writer.writeUTF("yourChips " + playerChips[playerID-1]);
				writer.writeUTF("tableUpdate yourBet " + betsAllGame[playerID-1]);	
				sendToAll("tableUpdate playerBet " + playerID + " " + betsThisRound[playerID-1]);
				sendToAll(color("&nbsp; Player <b>" + players[playerID-1] + "</b> played <i>Call</i>.", "white"));

			} else if (action.equals("raise")) {
				int maxBet = maxBetThisRound();
				int thisMoveBet = maxBet + amountInt - betsThisRound[playerID-1];
				
				if (thisMoveBet >= playerChips[playerID-1] && (maxBetHolder != playerID || currentRound == 1)) {
					writer.writeUTF("actionError");
					return false;
				}

				gamePot += thisMoveBet;
				betsThisRound[playerID-1] = maxBet + amountInt;
				betsAllGame[playerID-1] += thisMoveBet;
				playerChips[playerID-1] -= thisMoveBet;
				currentRoundPot += thisMoveBet;
				maxBetHolder = playerID;
				movesThisRound += 1;

				sendToAll("tableUpdate potAndMaxBet " + gamePot + " " + maxBetThisRound());
				writer.writeUTF("actionOk");
				writer.writeUTF("yourChips " + playerChips[playerID-1]);
				writer.writeUTF("tableUpdate yourBet " + betsAllGame[playerID-1]);	
				sendToAll("tableUpdate playerBet " + playerID + " " + betsThisRound[playerID-1]);
				sendToAll(color("&nbsp; Player <b>" + players[playerID-1] + "</b> played <i>Raise ($" + amount + ")</i>.", "white"));

			} else if (action.equals("allIn")) {
				int allPlayerChips = playerChips[playerID-1];
				
				if (allPlayerChips < 1) {
					writer.writeUTF("actionError");
					return false;
				}
				
				gamePot += allPlayerChips;
				betsThisRound[playerID-1] += allPlayerChips;
				betsAllGame[playerID-1] += allPlayerChips;
				playerChips[playerID-1] = 0;
				playerStatus[playerID-1] = false;
				numberOfCurrentPlayers -= 1;
				numberOfAllInPlayers += 1;

				sendToAll("tableUpdate potAndMaxBet " + gamePot + " " + maxBetThisRound());
				writer.writeUTF("actionOk");
				writer.writeUTF("yourChips 0");
				writer.writeUTF("tableUpdate yourBet " + betsAllGame[playerID-1]);	
				sendToAll("tableUpdate playerBet " + playerID + " " + betsThisRound[playerID-1]);
				sendToAll("tableUpdate allIn " + playerID);
				sendToAll(color("&nbsp; Player <b>" + players[playerID-1] + "</b> played <i>All-in</i>.", "white"));

				if (numberOfCurrentPlayers == 1) {
					currentRound = 7;
					movesThisRound = 1;
				}
			} else if (action.equals("fold")) {
				playerChips[playerID-1] = 0;
				playerStatus[playerID-1] = false;
				
				String playerMove = players[playerID-1];
				
				players[playerID-1] = null;
				numberOfCurrentPlayers -= 1;
				numberOfPlayersWhoFolded += 1;
				
				writer.writeUTF("disableMoves");
				sendToAll(color("&nbsp; Player <b>" + playerMove + "</b> left the game :(", "red"));
				sendToAll("actionOk");
				sendToAll("tableUpdate fold " + playerID);
				
				if (numberOfCurrentPlayers == 1) {
					currentRound = 7;
					movesThisRound = 1;
				}
			} else {
				movesThisRound += 1;
			}
			
			
			playerMoves[playerID-1] = new String[]{action, amount};
			return true;
		} else {
			if (playerMoves[playerID-1][0] != null) {
				return false;
			} else if (action.equalsIgnoreCase("dontDrawMyCards")) {
				writer.writeUTF("dontDrawCardsSuccess");
				sendToAll(color("&nbsp; Player <b>" + players[playerID-1] + "</b> didn't draw any card.", "#9f9696"));
			} else {
				writer.writeUTF("drawCardsSuccess");
			}
			
			
			playerMoves[playerID-1] = new String[]{action, amount};
			movesThisRound += 1;
			return true;
		}
	}
	
	
	/** 
	 * Register a 'fold' when a player turn off the application or connection
	 * will be lost.
	 * @param ID Player's ID who "folded".
	 * @param playerName Player's name.
	 */
	/*
	static void registerPseudoFold(int ID, String playerName) {
		
		if (playerStatus[ID-1] == true) {
			numberOfCurrentPlayers -= 1;
		} else {
			numberOfAllInPlayers -= 1;
		}
		
		playerChips[ID-1] = 0;
		playerStatus[ID-1] = false;
		players[ID-1] = null;
		numberOfPlayersWhoFolded += 1;
		
		sendToAll("<span style=\"color:red\">&nbsp; Player <b>"+playerName+"</b> left the game :(</span>");
		
		
		if (numberOfCurrentPlayers == 1) {
			currentRound = 7;
			movesThisRound = 1;
		} else if (ID == nextPlayerMoveID && currentRound % 2 == 1) {
			int id = nextPlayer(nextPlayerMoveID);
			sendToAll("nextPlayerMove " + id + " " + players[id-1] + " "
					+ possiblePlayerMoves(id));
			
		} else if (currentRound % 2 == 0) {
			if (playerMoves[ID-1][0] != null) {
				movesThisRound -= 1;
			}
		}
		
		playerMoves[ID-1] = new String[]{"fold", "0"};
	}

	
	/** 
	 * Chooses a winner of current game.
	 */
	/*
	static void chooseWinner() {
		String result = "";
		String[] allHands = new String[numberOfPlayers];
		
		for (int i = 0; i < numberOfPlayers; i++) {
			String[][] tmp = new String[4][3];
			
			if (playerStatus[i] == false && players[i] == null) {
				allHands[i] = "xxxx";
				
			} else { 
				for (int j = 0; j < 4; j++)
					tmp[j] = playerCards[i*4+j].split("-");
			
				allHands[i] = encodeHand(organizeCards(tmp));
			}
			
			System.out.println(" * " + players[i] + "'s hand code: " + allHands[i]);
		}
		
		String cmp = allHands[0];
		int dupl = 0;
		int winnerID = 1;
		
		
		for (int i = 0; i+1 < allHands.length; i++) {
			if (cmp.compareTo(allHands[i+1]) == 0) {
				dupl += 1;
			} else if (cmp.compareTo(allHands[i+1]) > 0) {
				cmp = allHands[i+1];
				winnerID = i+2;
				dupl = 0;
			}
		}
		

		
		if (dupl == 0) {
			
			if (playerStatus[winnerID-1]) {
				playerChips[winnerID-1] += gamePot;
			} else {
				int awardChips = 0;
				
				for (int i = 0; i < numberOfPlayers; i++) {
					if (i == winnerID-1)
						continue;
					
					if (playerChips[i] >= betsAllGame[winnerID-1]) {
						awardChips += betsAllGame[winnerID-1];
						playerChips[i] -= betsAllGame[winnerID-1];
					} else {
						awardChips += betsAllGame[i];
						playerChips[i] = 0;
					}
				}
				
				playerChips[winnerID-1] += awardChips + betsAllGame[winnerID-1];
			}

			result = "winner " + winnerID + " " + players[winnerID-1] + " " + playerChips[winnerID-1];
		} else {
			result = "tie";
		}
		
		String playersCanPlayAgain = prepareNewGame();
		sendToAll("endOfGame " + result);
		sendToAll("playersCanPlayAgain" + playersCanPlayAgain);
		
		if (allowNewPlayers) {
			try {
				listen();
			} catch (Exception e) {}
		}
	}
	
	
	/** 
	 * Changes an array (output from 'arraySort') to String.
	 * @param cardsValues Array of sorted integer values of cards.
	 * @return Returns a hand code, for example: "xxkc" for input array
	 * [3, 11, 0, 0]
	 */
	static String encodeHand(int[] cardsValues) {
		String[] chars = new String[]{"x","a","b","c","d","e","f","g","h","i","j","k","l","m"};
		String handCode = "";
		
		for (int i = cardsValues.length-1; i >= 0 ; i--) {
			handCode += chars[cardsValues[i]];
		}
		
		return handCode;
	}
	
	
	/** 
	 * Sorts cards.
	 * @param unsorted Array of cards in the same format as in method 'organizeCards'.
	 * @return Returns sorted array of cards in the same format as input array.
	 */
	static private String[][] sortCards(String[][] unsorted) {
		String[][] sorted = new String[4][3];
		int j = 0, index = 0;
		
		for (int i = 0; i < unsorted.length; i++) {
			int min = 15;
			
			for (j = 0; j < unsorted.length; j++) {
				if (unsorted[j][0] != null) {
					if (Integer.parseInt(unsorted[j][0]) < min) {
						min = Integer.parseInt(unsorted[j][0]);
						index = j;
					}
				}
			}
			
			sorted[i] = unsorted[index];
			unsorted[index] = new String[3];
		}
		
		return sorted;
	}
	
	
	/** 
	 * Sorts and prepares an array of cards.
	 * @param array And array of cards in format: [[VALUE], [COLOR-SHAPE]]
	 * (example: [[0] => "13", [1] => "Red-Diamond"])
	 */
	static int[] organizeCards(String[][] array) {
		String[][] sortedCards = sortCards(array);
		String[][] cards = sortedCards.clone();
		int[][] hands = new int[2][4];


		for (int k = 0; k <= 1; k++) {
			String values = "";
			String shapes = "";
			int[] numeric = new int[4];
			
			for (int i = 0; i <= 3; i++) {
				int min = 100;
				int minIndex = -1;
				
				for (int j = 0; j <= 3; j++) {
					if (cards[j][0] == null)
						continue;
					
					if (values.contains("["+cards[j][0]+"]") || shapes.contains(cards[j][2])) {
						cards[j] = new String[3];
					} else if (Integer.parseInt(cards[j][0]) < min) {
						min = Integer.parseInt(cards[j][0]);
						minIndex = j;
					}
				}
				
				if (minIndex == -1) {
					values += "[0]";
					numeric[i] = 14;
					continue;
				}
				
				values += "["+cards[minIndex][0]+"]";
				numeric[i] = Integer.parseInt(cards[minIndex][0]);
				shapes += cards[minIndex][2];
				cards[minIndex] = new String[3];
			}
			
			
			for (int l = 0; l <= 3; l++) {
				cards[l] = sortedCards[3-l];
			}
			
			hands[k] = numeric;
		}
		
		int[] cardsValues = hands[0];
		
		for (int i = 3; i >= 0; i--) {
			if (hands[0][i] > hands[1][i])
				cardsValues = hands[1];
				break;
		}
		
		return cardsValues;
	}
	
	
	/** 
	 * Adds a put card to put deck. 
	 * @param deck 
	 * @param card A card to add to put deck.
	 */
	private static void extendPutDeck(String[] deck, String card) {
		String[] newDeck = new String[deck.length+1];
		
		for (int i = 0; i < deck.length; i++)
			newDeck[i] = deck[i];
		
		newDeck[deck.length] = card;
		
		putDeck = new String[newDeck.length];
		putDeck = newDeck;
	}
	
	
	/** 
	 * When a player wants to draw a(/some) card(/s). 
	 */
	synchronized static void drawCards(int playerId, String line) {
		String[] tab = line.split(" ");
		
		for (int i = 1; i < tab.length; i++) {
			int index = Integer.parseInt(tab[i]) + (playerId-1)*4 - 1;
			
			if (currentDeck.length == 0) {
				currentDeck = shuffleDeck(putDeck);
				putDeck = new String[0];
			}
			
			// Zapamietaj stara karte
			// Pobierz nowa karte z nowej talii
			String oldCard = playerCards[index];
			String newCard = currentDeck[0];
			
			// Usun pobrana karte z talii
			// Wymien karte na nowa
			currentDeck[0] = null;
			playerCards[index] = newCard;
			
			// Usun puste miejsce po pobranej karcie z obecnej talii
			// Dopisz wymieniona karte do odlozonej talii
			sliceAllNulls(currentDeck);
			extendPutDeck(putDeck, oldCard);
		}
	}
	
	
	/**
	 * Change input String to HTML object with color set in inline style.
	 */
	private static String color(String str, String color) {
		return "<span style=\"color:" + color + ";\">" + str + "</span>";
	}
	
  
}
